-module(subscriber_mac).
-hehaviour(gen_server).
-include("subscriber.hrl").
-include("protocol.hrl").
-define(SERVER,?MODULE).
-define(session_timeout,1000).
-export([do/2,do/3]).
-export([start/0,start_link/0,start_link/1,init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2,code_change/3]).

do(create,Sub) ->
	{ok,Pid} = subscriber_sup:start_child(Sub),
	Pid.
	
do(forward,Pid,Packet) when is_pid(Pid) ->
	gen_server:cast(Pid, {packet_comming,Packet}).

start()  ->
	gen_server:start({local, ?SERVER}, ?MODULE, [], []).
	
start_link(Type) ->
	gen_server:start_link(?MODULE, [Type], []).	
	
start_link() ->
	start_link([wlan]).
	
init([Sub]) ->
	sub_dict:do(create,{mac,Sub#subscriber.mac,self()}),
	{ok,Timer} = timer:apply_after(?session_timeout,gen_server,call,[self(),timeout]),
	{ok,{Timer,Sub}}.
	
	
handle_call({set,mac,Mac},_,{Timer,Sub}) ->
	timer:cancel(Timer),
	{ok,NTimer} = timer:apply_after(?session_timeout,gen_server,call,[self(),timeout]),
	{reply, ok, {NTimer,Sub#subscriber{mac = Mac}}};

handle_call(timeout,_,{Timer,Sub}) ->
	transition(Sub#subscriber{end_reason="timeout"},wlan,ending),
	{stop,timeout,{}};
	
handle_call(_,_,State) ->
	{reply, {error,"no func define"}, State}.


handle_cast({packet_comming,#sub_packet{dir=Dir,prot = i80211mgt, packet=Packet, ran_info = Wlan_Spec}},{Timer,Sub}) ->
	timer:cancel(Timer),
	{ok,NTimer} = timer:apply_after(?session_timeout,gen_server,call,[self(),timeout]),
	<<SubType:4,0:2,_Ver:2,_OFlags:6,_D:2,_Duration:16,D_MAC:6/binary,S_MAC:6/binary,BSSID:6/binary,_Other/binary>> = Packet,
	NewSub = case {Dir,SubType,Sub#subscriber.state} of
		{up,0,initial} ->	%% associate request
			transition(Sub,wlan,{starting,BSSID,D_MAC});
		{_,0,_} -> %% association request
			transition(Sub#subscriber{end_reason="force_logout, or timeout"},wlan,ending),	%% end the call and start a new one
			transition(Sub,wlan,{starting,BSSID,D_MAC});
		{down,1,starting} ->
			transition(Sub,wlan,unauthorized);
		{_,Msg,_} ->
			Sub
		end,
	{noreply,{NTimer,NewSub#subscriber{ran_info = (NewSub#subscriber.ran_info)#sub_wlan{ap = Wlan_Spec#wlan_spec.ap_ip,ac = Wlan_Spec#wlan_spec.ac_ip}}}};

handle_cast({packet_comming,#sub_packet{dir=Dir,prot = llc, packet=Packet}},{Timer,Sub}) ->
	timer:cancel(Timer),
	{ok,NTimer} = timer:apply_after(?session_timeout,gen_server,call,[self(),timeout]),
	<<_LLC_TAG:48,Prot:16,Payload/binary>> = Packet,
	{noreply,{NTimer,sub_traffic:analysis(#sub_packet{dir=Dir,prot=Prot,packet=Payload},Sub)}};

handle_cast({packet_comming,#sub_packet{dir=Dir,prot = radius, packet=Packet}},{Timer,Sub}) ->
	timer:cancel(Timer),
	{ok,NTimer} = timer:apply_after(?session_timeout,gen_server,call,[self(),timeout]),
	{noreply,{NTimer,Sub}};
	
	
handle_cast({packet_comming,#sub_packet{dir=Dir,prot = Protocol, packet=Packet}},{Timer,Sub}) ->
	timer:cancel(Timer),
	{ok,NTimer} = timer:apply_after(?session_timeout,gen_server,call,[self(),timeout]),
	{noreply,{NTimer,Sub}};
	
handle_cast(Info, State) ->
	io:format("received unknown info ~p ~n",[Info]),
	{noreply,State}.
handle_info(_, State) ->
	{noreply,State}.
terminate(_, _) ->
	ok.
code_change(_, State, _) ->
	{ok,State}.
	
transition(Sub,wlan,{starting,BSSID,DMAC}) ->
	Sub#subscriber{
		type=wlan,
		state=starting,
		ran_info = #sub_wlan{
			bssid = BSSID,
			ap = DMAC
		}
	};
transition(Sub,wlan,unauthorized) ->
	Sub#subscriber{
		state = unauthorized};

transition(Sub,wlan,ending) ->
	gen_event:notify(cdr_event,{cdr,Sub}),
	Sub#subscriber{type=wlan}; %% a new sub record

transition(Sub,_,_) ->
	Sub.	

	